/*
* Copyright (c) 2010 Michael Collins
*
* This file is part of TerraFirma.
*
* TerraFirma is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* TerraFirma is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with TerraFirma.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NETWORK_THREAD_H
#define NETWORK_THREAD_H

#include <task_thread.h>
//#include <logic_thread.h>
#include "message_logic.h"
#include "message_network.h"
#include "network_pipe.h"
#include <message_queue.h>
#include <iostream>

//Evaluates to true if sequence# n is a newer packet than previous sequence# pn
#define IS_NEW_SEQ(n, pn) ((uint16_t)((pn) - (n)) > 0x8000)

namespace engine
{
    //This acts as the network thread on hosts that are servers
    //You cannot run both server and client thread at the same time!!
    class network_thread : public task_thread<local_network_msg>
    {
        //This is an asyncronous server
    public:
        #if defined(SERVER) || defined(LISTEN)
        network_thread(boost::asio::io_service& io_service, short port);
        #else
        network_thread(boost::asio::io_service& io_service);
        bool connect(const char* hostname, short port);
        #endif

        ~network_thread();
        const char* name()
        {
            return "network_thread";
        }

        void F(const boost::thread::id tid);

    protected:
    private:
        network_pipe netpipe;

        //Blocks indefinitely until a message comes up;
        void wait();

        //Function for convenience to start an asyncronous read of the network port
        void receive()
        {
            //std::cout << "Receive called." << std::endl;
            sock.async_receive_from(
                boost::asio::buffer(&data, sizeof(data)),
                sender,
                boost::bind(&network_thread::handle_receive_from, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
        }

        void send_message(const inter_network_msg& m);

        //Function for convenience to start an asyncronous write to the network port
        void send(boost::asio::ip::udp::endpoint& ep, const void* d, size_t length)
        {
            sock.async_send_to(
                boost::asio::buffer(d, length), ep,
                boost::bind(&network_thread::handle_send_to, this,
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred));
        }

        void handle_receive_from(const boost::system::error_code& error, size_t bytes_received);

        void handle_send_to(const boost::system::error_code& error, size_t bytes_sent);

        void process_remote_message( const message_header& header_msg );

        void process_message(const local_network_msg& msg);
        boost::asio::io_service& service;
        boost::asio::ip::udp::socket sock;
        boost::asio::ip::udp::endpoint sender; //Object to store the remote client endpoint
        #ifdef CLIENT
        boost::asio::ip::udp::endpoint server; //Object representing the remote server endpoint
        #endif

        inter_network_msg data; //Buffer for received data to go into

        uint16_t recv_seqs [256];

        bool running;
    };

}

#endif // NETWORK_THREAD_H
